package main

import (
	"fmt"
	"log"
	"context"
	"os"
	"strconv"
	"go.mongodb.org/mongo-driver/bson"    //BOSN解析包
	"go.mongodb.org/mongo-driver/mongo"    //MongoDB的Go驱动包
	"go.mongodb.org/mongo-driver/mongo/options"
)

type People struct {
	Name string `json:"Name" bson:"Name"`
	Age  int `json:"Age" bson:"Age"`
}

func checkArgs(arg, num int) bool {
	if arg < num {
		help()
		return false
	}
	return true
}
func help() {
	fmt.Println("./demo 0 - help");
	fmt.Println("./demo 1 [name] - findall");
	fmt.Println("./demo 2 [name] - insert");
	fmt.Println("./demo 3 [name] - delete");
	fmt.Println("./demo 4 [name] - findone");
	fmt.Println("./demo 5 [name] - updateone");
	return
}
func main() {
	fmt.Println("main args(num =", len(os.Args), "):", os.Args)
	if !checkArgs(len(os.Args), 2) {
		return
	}
	x, _ := strconv.Atoi(os.Args[1])

	clientDB := connectDB()
	collection := createCollection(clientDB)

	switch x {
	case 0 :
		help()
	case 1 :
		findDB(collection)
	case 2:
		if !checkArgs(len(os.Args), 3) {
			fmt.Println("ERROR : insert !")
			break	
		}
		insertDB(collection, os.Args[2])
		findDB(collection)
	case 3:
		if !checkArgs(len(os.Args), 3) {
			fmt.Println("ERROR : delete !")
			break	
		}
		deleteDB(collection, os.Args[2])
		findDB(collection)
	case 4:
		if !checkArgs(len(os.Args), 3) {
			fmt.Println("ERROR : findone !")
			break	
		}
		findOneDB(collection, os.Args[2])
	case 5:
		if !checkArgs(len(os.Args), 4) {
			fmt.Println("ERROR : updateone !")
			break	
		}
		fmt.Println("========Before updateone !")
		findDB(collection)
		fmt.Println("========Begin updateone !")
		age, _ := strconv.Atoi(os.Args[3])
		updateOneDB(collection, os.Args[2], age)
		fmt.Println("========After updateone !")
		findDB(collection)
	}
	disconnectDB(clientDB)
	return
}

func connectDB() *mongo.Client {
	// Set client options
	clientOptions := options.Client().ApplyURI("mongodb://127.0.0.1:27017")
	// Connect to MongoDB, clint 就是与 mongodb 的连接对象
	client, err := mongo.Connect(context.TODO(), clientOptions)
	if err != nil {
		log.Fatal(err)
	}
	// Check the connection
	err = client.Ping(context.TODO(), nil)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Connected to MongoDB!")
	return client
}

func disconnectDB(clientDB *mongo.Client) {
	err := clientDB.Disconnect(context.TODO())
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Connection to MongoDB closed.")
}

func createCollection(clientDB *mongo.Client) *mongo.Collection {
	// collect 是要操作的数据集(相当于表), 
	// 此处指向的数据库是 test, 数据表示 aaa
	collection := clientDB.Database("orcafs").Collection("col1")
	fmt.Println("create DB test and table aaa")
	return collection
}

func insertDB(collection *mongo.Collection, name string) {
	bob := People{name, 10}
	insertResult, err := collection.InsertOne(context.TODO(), bob)
	fmt.Println("insertOne return:", err)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Inserted a single document: ", insertResult.InsertedID)
}

// 删除不存在的值也不会报错
func deleteDB(collection *mongo.Collection, name string) {
	_, err := collection.DeleteOne(context.TODO(), bson.D{{"Name", name}})
	if err != nil {
		log.Fatal(err)
	}
}

// 找不到返回 err : mongo: no documents in result
func findOneDB(collection *mongo.Collection, name string) {
	var p People
	err := collection.FindOne(context.TODO(), bson.D{{"Name", name}}).Decode(&p)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(p)
}

// 没有找到也不会报错，因此要判断查询结果的个数
func findDB(collection *mongo.Collection) {
	// Passing bson.D{{}} as the filter matches all documents in the collection 
	record, err := collection.Find(context.TODO(), bson.D{{}})
	if err != nil {
		log.Fatal(err)
	}
	defer func() {
		if err := record.Close(context.TODO()); err != nil {
			log.Fatal(err)
		}
	}()

	allRecord := []People{}
	_ = record.All(context.TODO(), &allRecord) //也可以用 next
	for _, r := range allRecord {
		fmt.Println(r)
	}
}

func updateOneDB(collection *mongo.Collection, name string, age int) {
	p := People{name, age}
	ifInsert := true	
	option := options.UpdateOptions{Upsert: &ifInsert}
	fmt.Println("updateOne context is :", p)
	_, err := collection.UpdateOne(context.TODO(),
	                               bson.M{"Name": name},
								   bson.M{"$set": p},
								   &option)
	fmt.Println("updateOne return:", err)
}

